﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
namespace DataProcessing
{
    public static class BedFileParser
    {
        public static long currReadPos = 0;
        public static long fileLength = 0;
        public static double processedValue = 0;
        public static long bufferLen
        {
            get
            {
                return _fileBuffer.Count;
            }
        }
  
        class Context
        {
            public int start;
            public int end;
            public int index;
        }
        static List<string> _fileBuffer = new List<string>();

        static List<ChrSampling>[] _parsingBuffer;
       
        static void CleanBuffer()
        {
            _fileBuffer = new List<string>();
            _parsingBuffer = new List<ChrSampling>[CpuCount];
        }
        
        public static long GetFileLength(string fileName)
        {
            FileStream stream = new FileStream(fileName, FileMode.Open);
            long len = stream.Length;
            stream.Close();
            return len;
        }
        static void ReadData(string fileName)
        {
              
            FileStream stream = new FileStream(fileName,FileMode.Open);
            fileLength = stream.Length;
            StreamReader reader = new StreamReader(stream);
            
            string line =reader.ReadLine();
                  
            DateTime t1 = DateTime.Now;
            
            try
            {
                _fileBuffer.Clear();
                while (line != null)
                {
                    _fileBuffer.Add(line);
                    line = reader.ReadLine();
                    currReadPos = stream.Position;
                }
            }
          
            catch(Exception ex)
            {
                reader.Close();
                stream.Close();
                //throw new Exception("parsing failed for file"+fileName+":\n"+ex.ToString());
            }
          
            DateTime t2 = DateTime.Now;
            double elapse = (t2 - t1).TotalMilliseconds;
            reader.Close();
            stream.Close();
            return;

        }

        /// <summary>
        /// helper function.用于测试，从bed文件里抽取某个特定基因的数据
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="index"></param>
        public static void Extract(string fileName, int index)
        {
            ChromePairData.Data = GetFromFile(fileName);
            ChromePairData dataToExtract = ChromePairData.Data[index-1];
            string newFileName =Path.GetDirectoryName(fileName)+"\\"+Path.GetFileNameWithoutExtension(fileName) + "_"+index.ToString() + ".bed";
            
            FileStream stream = new FileStream(newFileName, FileMode.Create);

            StreamWriter writer = new StreamWriter(stream);
            string prefix = "chr" + index.ToString();
            foreach (ChrSampling sampling in dataToExtract.ChromeSampling)
            {
                string lineToWrite ="";
                lineToWrite =  string.Format("{0}\t{1}\t{2}\t{3}",prefix,sampling.Begin.ToString(),sampling.End.ToString(),sampling.Direction?"+":"-");
                writer.WriteLine(lineToWrite);
            }
            writer.Close();
        }

        static void Parse(Context ctx)
        {
            //注意，必须在这里new，不要声明全局静态
            //因为regex是线程安全，故此多线程共享一个会有互斥开销。
            Regex _pattern = new Regex(@"\s*chr(?<chromeIndex>\d+|Y|X)\s+(?<start>\d+)\s+(?<end>\d+)",RegexOptions.Compiled|RegexOptions.Singleline|RegexOptions.IgnoreCase);
            _parsingBuffer[ctx.index] = new List<ChrSampling>();
            List<ChrSampling> ret = _parsingBuffer[ctx.index];

            for (int i = ctx.start; i <= ctx.end; i++)
            {
                GroupCollection matches = _pattern.Match(_fileBuffer[i]).Groups;
                if (matches.Count != 4)
                {
                    //throw new Exception("bad format at:" + i.ToString() + ":" + buffer[i]);
                    processedValue++;//不能识别就跳过
                    continue;
                }
                ChrSampling sampling = new ChrSampling();
                sampling.Begin = uint.Parse(matches["start"].Value);
                sampling.End = uint.Parse(matches["end"].Value);
                string chrIndex = matches["chromeIndex"].Value;

                sampling.ChrIndex =(byte) (Settings.Instance.ChromeSettings[chrIndex.ToLower()].Index);
                ret.Add(sampling);
                processedValue++;
            }
            NotifyThreadFinish();
        }
        public static int CpuCount = 4;


        static void ThreadProc(Object stateInfo)
        {
            Context ctx = stateInfo as Context;
            Parse(ctx);
        }


        static AutoResetEvent _parseFinishEvent;// = new AutoResetEvent(false);
        static int _finishCount = 0;
        static void ResetParallelCtx()
        {
            _parseFinishEvent = new AutoResetEvent(false);
            _finishCount = 0;
          
        }
        static void DetectCpuCount()
        {
            CpuCount = 0;
            foreach (var item in new System.Management.ManagementObjectSearcher("Select * from Win32_Processor").Get())
            {
                CpuCount += int.Parse(item["NumberOfCores"].ToString());
            }

        }
        public static bool _parsingBegin = false;
        static void Reset()
        {
            DetectCpuCount();
            _parsingBuffer = new List<ChrSampling>[CpuCount];
            ResetParallelCtx();
            processedValue = 0;
            _parsingBegin = false;
        }
        public static ChromePairData[] GetFromFile(string fileName)
        {
            

            Reset();

            DateTime t1 = DateTime.Now;
            ReadData(fileName);
            DateTime t2 = DateTime.Now;
            double elapse = (t2 - t1).TotalMilliseconds;
            ChromePairData[] ret =new ChromePairData[Settings.Instance.ChromeCount];
            int i =0;
            for(i=0;i<ret.Length;i++)
            {
                ret[i] = new ChromePairData();
                ret[i].Number = (uint)i;
                ret[i].Name = Settings.Instance.ChromeSettingList[i].Name;
            }

            processedValue = 0;
            int size = (_fileBuffer.Count%CpuCount==0)?_fileBuffer.Count/CpuCount:(_fileBuffer.Count/CpuCount+1);
            for(i=0;i<CpuCount;i++)
            {
                Context ctx = new Context();
                ctx.start =i * size;
                ctx.end = Math.Min(ctx.start + size - 1,_fileBuffer.Count-1);
                ctx.index = i;
                ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc), ctx);
            }
            _parsingBegin = true;
            _parseFinishEvent.WaitOne();
            DateTime t3 = DateTime.Now;
            double elapse1 = (t3 - t2).TotalMilliseconds;
            for(int j=0;j<CpuCount;j++)
            {
                for (i = 0; i < _parsingBuffer[j].Count; i++)
                {
                    ChromePairData data = ret[_parsingBuffer[j][i].ChrIndex];
                    data.AddSample(_parsingBuffer[j][i]);
                    
                }
            }
            
            
           
            CleanBuffer();
            return ret;
        }

        static object _lock = new object();
        static void NotifyThreadFinish()
        {
            lock (_lock)
            {
                _finishCount++;
                if (_finishCount == CpuCount)
                {
                    _parseFinishEvent.Set();
                }
            }
        }

    }
}
